package com.md.application.service.role;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import com.md.api.authentication.RoleApi;
import com.md.application.assembler.RoleApiAssembler;
import com.md.application.assembler.RoleAssembler;
import com.md.client.system.SystemClient;
import com.md.domain.model.bo.BindRoleBo;
import com.md.domain.model.bo.QueryRoleBo;
import com.md.domain.model.bo.RoleBo;
import com.md.domain.model.po.Role;
import com.md.domain.model.po.RoleApis;
import com.md.domain.service.AuthenticationDomainService;
import com.md.exception.ServerException;
import com.md.model.bo.ServiceContextBo;
import com.md.model.bo.SizeBo;
import com.md.model.po.Apis;
import com.md.model.request.IdRequest;
import com.md.model.request.authentication.role.BindRoleApiRequest;
import com.md.model.request.authentication.role.InsertRoleRequest;
import com.md.model.request.authentication.role.QueryRoleRequest;
import com.md.model.request.authentication.role.UpdateRoleRequest;
import com.md.model.request.system.system.QueryDictionariesRequest;
import com.md.model.response.authentication.role.RoleDetailResponse;
import com.md.model.response.authentication.role.RoleResponse;
import com.md.model.response.system.DictionariesDetailsResponse;
import com.md.model.system.CommonResult;
import com.md.model.system.PageResponse;
import com.md.repository.RoleApiRepository;
import com.md.repository.RoleRepository;
import com.md.strategy.Strategy;
import com.md.util.ClassUtils;
import com.md.util.CollectionUtil;
import com.md.util.RedisUtils;
import com.md.util.ThreadUtil;
import com.md.value.constants.RedisCacheConstants;
import com.md.value.enums.DictionaryEnum;
import com.md.value.enums.ErrorEnum;
import com.md.value.enums.StrategyEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 刘骄阳
 * 2022-05-31 22:50
 */
@Service
@RequiredArgsConstructor
public class RoleApplicationService implements RoleApi {
  private final AuthenticationDomainService authenticationDomainService;
  private final RoleRepository roleRepository;
  private final RoleApiRepository roleApiRepository;
  private final RedisUtils redisUtils;
  private final SystemClient systemClient;


  /**
   * 添加角色
   *
   * @param request 角色请求实体
   * @return 操作结果
   */
  @Override
  public CommonResult<Long> insert(final InsertRoleRequest request) {
    RoleAssembler.buildInsertRoleRequest(request);
    RoleBo roleBo = authenticationDomainService.buildRoleBo(request.getName(), request.getParentId());
    Role role = roleBo.getRole();
    // 执行新增
    roleRepository.insert(role);
    ServiceContextBo serviceContextBo = ServiceContextBo.buildServiceContextBo();
    ThreadUtil.runAsync(() -> {
      ServiceContextBo.putAll(serviceContextBo);
      Strategy.getInstance(StrategyEnum.ROLE).asyncAfter(roleBo);
    }, "新增角色");
    return CommonResult.success(role.getId());
  }

  /**
   * 修改角色
   *
   * @param request 请求实体
   * @return 操作结果
   */
  @Override
  @CacheEvict(value = RedisCacheConstants.ROLE_QUERY_ROLE_INFO, key = "#request.id")
  public CommonResult<Boolean> update(final UpdateRoleRequest request) {
    if (roleRepository.hasExists(Role.builder().name(request.getName()).build())) {
      throw ServerException.build(ErrorEnum.ROLE_HAS_EXISTS);
    }
    Role role = ClassUtils.source2Target(request, Role.class);
    roleRepository.update(role);
    return CommonResult.success(Boolean.TRUE);
  }

  /**
   * 绑定角色与权限
   *
   * @param request 角色权限绑定请求实体
   * @return 修改结果(true : 成功 ; false : 失败)
   */
  @Override
  @CacheEvict(value = RedisCacheConstants.ROLE_QUERY_ROLE_INFO, key = "#request.roleId")
  public CommonResult<Boolean> bindRoleApi(final BindRoleApiRequest request) {
    List<RoleApis> roleApis = roleApiRepository.queryByRoleIdOrApiId(null, request.getRoleId());
    List<Long> apiIds = roleApis.stream().map(RoleApis::getApiId).collect(Collectors.toList());
    List<Apis> apisList = authenticationDomainService.getApiRepository().selectByIds(apiIds);
    BindRoleBo bindRoleBo = RoleApiAssembler.buildBindRoleBo(roleApis, request, apisList);
    roleApiRepository.deleteByRoleIdAndApiIds(Lists.newArrayList(bindRoleBo.getRoleId()),
      bindRoleBo.getDeleteApiId(), null);
    roleApiRepository.insertBatch(RoleApiAssembler.buildInsertRoleApi(request.getRoleId(), request.getApiList()));

    ServiceContextBo serviceContextBo = ServiceContextBo.getAll();
    ThreadUtil.runAsync(() -> {
      ServiceContextBo.putAll(serviceContextBo);
      authenticationDomainService.bindAfter(bindRoleBo);
    }, "重新绑定角色信息后,异步删除角色缓存");
    return CommonResult.success(Boolean.TRUE);
  }

  /**
   * 查询角色详情
   *
   * @param request 查询角色详情请求实体
   * @return 角色详情
   */
  @Override
  @Cacheable(value = RedisCacheConstants.ROLE_QUERY_ROLE_INFO, key = "#request.id")
  public CommonResult<RoleDetailResponse> query(final IdRequest request) {
    RoleDetailResponse roleDetailResponse = authenticationDomainService.queryRoleInfo(request.getId());
    return CommonResult.success(roleDetailResponse);
  }

  /**
   * 查询角色列表
   *
   * @param request 查询角色列表请求实体
   * @return 角色列表
   */
  @Override
  public CommonResult<PageResponse<RoleResponse>> selectPage(final QueryRoleRequest request) {
    QueryRoleBo queryRoleBo = RoleAssembler.buildQueryRoleBo(request);
    authenticationDomainService.getRoleRepository().selectList(Role.builder().name(queryRoleBo.getName()).build());
    PageResponse<Role> pageResponse = authenticationDomainService.selectRolePage(queryRoleBo);
    IPage<Role> page = roleRepository.queryPage(queryRoleBo);
    List<Role> records = page.getRecords();
    List<Long> roleIds = CollectionUtil.map(records, Role::getId);
    DictionariesDetailsResponse dictionariesDetails = systemClient.queryDictionariesDetails(QueryDictionariesRequest.
      builder().key(DictionaryEnum.ROLE_ID.getMessage()).build()).getData();
    List<RoleResponse> list = RoleAssembler.buildRoleResponse(records, dictionariesDetails, CollectionUtil.
      toMap(roleApiRepository.querySize(roleIds), SizeBo::getGroupId, SizeBo::getSize));
    return CommonResult.success(PageResponse.of(pageResponse.getPages(), pageResponse.getTotal(),
      pageResponse.getPageSize(), pageResponse.getPageNum(), list));
  }
}
